﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Mesuro.Common {
	public class Matrix {
		public static Matrix Diagonal( params double[] vals ) {
			Matrix ret = new Matrix( vals.Length, vals.Length );
			for (int i = 0; i < vals.Length; i++) {
				ret[i, i] = vals[i];
			}
			return ret;
		}

		private readonly double[] data;
		private bool rowMajor;

		public int Rows { get; private set; }

		public int Cols { get; private set; }

		public double this[int row, int col] {
			get { return data[(rowMajor) ? (row * Cols + col) : (col * Rows + row)]; }
			set { data[(rowMajor) ? (row * Cols + col) : (col * Rows + row)] = value; }
		}

		public Matrix( int rows, int cols, bool initiallyRowMajor = true ) {
			if (rows < 1 || cols < 1) throw new ArgumentException( String.Format( "Cannot create a matrix with dimensions {0}x{1}", rows, cols ) );
			this.Rows = rows;
			this.Cols = cols;
			this.data = new double[rows * cols];
			rowMajor = initiallyRowMajor;
		}

		public Matrix( double[,] data, bool rowMajor ) {
			this.rowMajor = rowMajor;
			Rows = data.GetLength( rowMajor ? 0 : 1 );
			Cols = data.GetLength( rowMajor ? 1 : 0 );
			this.data = new double[Rows * Cols];
			for (int r = 0; r < Rows; r++) {
				for (int c = 0; c < Cols; c++) {
					this[r, c] = data[r, c];
				}
			}
		}

		private void Transpose() {
			rowMajor = !rowMajor;
			int b = Rows;
			Rows = Cols;
			Cols = b;
		}

		public static Vector operator *( Matrix mat, Vector vec ) {
			if (mat.Cols != vec.Size) throw new ArgumentException( String.Format( "Cannot post-multiply a matrix with {0} columns by a vector with {1} elements", mat.Cols, vec.Size ) );
			Vector ret = new Vector( mat.Rows );
			for (int i = 0; i < mat.Rows; i++) {
				for (int j = 0; j < mat.Cols; j++) {
					ret[i] += mat[i, j] * vec[j];
				}
			}
			return ret;
		}
		public static Vector operator *( Vector vec, Matrix mat ) {
			if (mat.Rows != vec.Size) throw new ArgumentException( String.Format( "Cannot pre-multiply a matrix with {0} rows by a vector with {1} elements", mat.Cols, vec.Size ) );
			Vector ret = new Vector( mat.Cols );
			for (int i = 0; i < mat.Rows; i++) {
				for (int j = 0; j < mat.Cols; j++) {
					ret[i] += mat[j, i] * vec[j];
				}
			}
			return ret;
		}
	}
}
